#include "BTreeNode.h"
using namespace std;

const int LEAF=42;
//bool isLeafNode(PageId pid, PageFile pf);

//leaf helper functions
static void readLeaf(const char* page, int n, int& key, RecordId& value);
static void writeLeaf(char* page, int n, int key, RecordId& value);
static char* LeafPtr(char* page, int n);
static void setLeafCount(char* page, int count);

//non leaf helper functions

static char* NonLeafPtr(char* page, int n);
static void readNonLeaf(const char* page, int n, int& key, PageId& value);
static void writeNonLeaf(char* page, int n, int key, PageId& value);
static void setNonLeafCount(char* page, int count);
static void getNonLeafPid(char* page, int& pid)	;
static void setNonLeafPid(char* page, int pid);


/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{ 
	RC rc;
	if((rc=pf.read(pid, buffer))<0)
	{
		return rc; 
	}
	return 0;
}
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{ 
	RC rc;
	memcpy((buffer+sizeof(int)), &LEAF, sizeof(int));
	
	int isleaf;
	memcpy(&isleaf, (buffer+sizeof(int)), sizeof(int));
	if((rc=pf.write(pid, buffer))<0)
	{
		return rc; 
	}
	return 0;


}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{ 
	int count;
	// the first four bytes of a page contains # records in the page
	memcpy(&count, buffer, sizeof(int));
	return count;
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid)
{ 
	
	int key2;
	RecordId rid2;
	RecordId irid;
	irid.pid=rid.pid;
	irid.sid=rid.sid;
	int pos=0;
	int insertPos;
	//int key2pos=0;
	int records=getKeyCount();
	while(pos<records)
	{
		readLeaf(buffer, pos, key2, rid2);
		if (pos==0 && key2>key)
		{
			insertPos=-1;
			break;
		}
		if (key2<key)
		{
			insertPos=pos;
		}
		pos++;
		
	}
	
	
	
	if (insertPos<records)
	{
		pos=(insertPos+1);
		records++;
		int key3;
		RecordId rid3;
		readLeaf(buffer, pos, key2, rid2); 
		writeLeaf(buffer, pos, key, irid);
		pos++;
		
		while (pos<records)
		{
			readLeaf(buffer, pos, key3, rid3);
			writeLeaf(buffer, pos, key2, rid2);
			key2=key3;
			rid2.pid=rid3.pid;
			rid2.sid=rid3.sid;
			pos++;
		}
		setLeafCount(buffer, records);
		return 0;
	}
	else //either the key is too large or the position is a the end
	{
		writeLeaf(buffer, records, key, irid);
		records++;
		setLeafCount(buffer, records);
	}
	
	
	return -1; 

}

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, 
                              BTLeafNode& sibling, int& siblingKey)
{ 
	int endptr=this->getNextNodePtr();
	
	
	int records=getKeyCount();
	int start=records/2;
	int key2;
	RecordId rid2;
	this->insert(key, rid);
	
	readLeaf(buffer, start, siblingKey, rid2);
	
	
	setLeafCount(sibling.buffer, 0);
	/*
	if(sibling.getKeyCount()!=0)
	{
		return -1;
	}
	*/
	 
	for (int i=start; i<records; i++)
	{
		readLeaf(buffer, i, key2, rid2);
		sibling.insert(key2, rid2); //insert is a large function i might want to use writeleaf
	}
	int newCount=(records-start);
	setLeafCount(buffer, newCount);
	this->setNextNodePtr(endptr);
	return 0; 
}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& eid)
{ 
	int key2;
	int pos=0;
	RecordId rid2;
	int records=getKeyCount();
	while(key2<searchKey && pos<records)
	{
		readLeaf(buffer, pos, key2, rid2);
		pos++;
	}
	eid=pos;
	return 0;

}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::readEntry(int eid, int& key, RecordId& rid)
{ 
	RC rc;
	readLeaf(buffer, eid, key, rid);
	{
		//return -1;
	}
	return 0; 
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{
	int count;
	// the last four bytes of a page contain the next pid
	memcpy(&count, buffer+(PageFile::PAGE_SIZE-sizeof(int)), sizeof(int));
	return count;
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{ 
	memcpy(buffer+(PageFile::PAGE_SIZE-sizeof(int)), &pid, sizeof(int));
	return 0;
}










/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{ 
	RC rc;
	if((rc=pf.read(pid, buffer))<0)
	{
		return rc; 
	}
	return 0;
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{ 
	RC rc;
	int notLeaf=1776;
	memcpy((buffer+sizeof(int)), &notLeaf, sizeof(int));
	if((rc=pf.write(pid, buffer))<0)
	{
		return rc; 
	}
	return 0;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{
	int count;
	// the first four bytes of a page contains # keys in the page
	memcpy(&count, buffer, sizeof(int));
	return count;
}


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(int key, PageId pid)
{ 
	int key2;
	PageId pid2;
	int pos=0;
	int records=getKeyCount();
	while(pos<records)
	{
		readNonLeaf(buffer, pos, key2, pid2);
		if (key<key2)
		{
			if(pos==0)
			{
			}
			break;
		}
		if (key==key2)
		{
			return -1;
		}
		
		
		
		pos++;
	}
	
	
	if (pos<records)
	{
		records++;
		int key3;
		PageId pid3;
		
		while (pos<records)
		{
			readNonLeaf(buffer, pos, key3, pid3);
			writeNonLeaf(buffer, pos, key, pid);
			pid=pid3;
			pos++;
		}
		setNonLeafCount(buffer, records);
		return 0;
	}
	else //either the key is too large or the position is a the end
	{
		
		writeNonLeaf(buffer, records, key, pid);
		records++;
		setNonLeafCount(buffer, records);
		return 0;
	}
	
	
	return -1;

	
	
}

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{ 
	insert(key, pid);
	
	int records=getKeyCount();
	int start=records/2;
	int key2;
	PageId pid2;
	readNonLeaf(buffer, start, midKey, pid2);
	
	if(sibling.getKeyCount()!=0)
	{
		return -1;
	}
	
	for (int i=start; i<records; i++)
	{
		readNonLeaf(buffer, i, key2, pid2);
		sibling.insert(key2, pid2);
	}
	setLeafCount(buffer, (records-start));
	return 0; 


}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{ 
	int key2;
	PageId pid2;
	pid=0;
	int pos=0;
	//int key2pos=0;
	int records=getKeyCount();
	while(pos<records)
	{
		readNonLeaf(buffer, pos, key2, pid2);
		if (searchKey<key2)
		{
			if (pos==0)
			{
				getNonLeafPid(buffer, pid);
				return 0;
			}
			
			readNonLeaf(buffer, pos-1, key2, pid2);
			pid=pid2;
			return 0;
		}
		if (searchKey>key2 && pid!=0)
		{
			return 0;
		}
		pos++;
	}
	if (pid!=0)
	{
		return 0;
	}
	
	if (pos==records && pid==0)
	{
		getNonLeafPid(buffer, pid);
		return 0;
	}
	return-1;
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{ 
	writeNonLeaf(buffer, 0, key, pid2);
	setNonLeafPid(buffer, pid1);
	setNonLeafCount(buffer, 1);
	return 0; 
}


		  
//leaf helper functions	

static char* LeafPtr(char* page, int n) 
{
	return (page+sizeof(int)+sizeof(int) + (sizeof(int)*3)*n);
}
		  
		  
static void readLeaf(const char* page, int n, int& key, RecordId& value)
{
	// compute the location of the record
	char *ptr = LeafPtr(const_cast<char*>(page), n);
	
	// read the key 
	memcpy(&key, ptr, sizeof(int));
	
	// read the RecordId components
	memcpy(&value.pid, ptr + sizeof(int), sizeof(int));
	memcpy(&value.sid, ptr + 2*sizeof(int), sizeof(int));
	//memcpy(&value.sid, ptr + sizeof(int) + sizeof(PageId), sizeof(int));
}

static void writeLeaf(char* page, int n, int key, RecordId& value)
{
	// compute the location of the record
	char *ptr = LeafPtr(page, n);
	
	// store the key
	memcpy(ptr, &key, sizeof(int));
	
	//store the RecordId
	memcpy(ptr + sizeof(int), &value.pid, sizeof(int));
	memcpy(ptr + 2*sizeof(int), &value.sid, sizeof(int));
	//cout << "writeleaf ptr: " << ptr << endl;
	
}

static void setLeafCount(char* page, int count)
{
	memcpy(page, &count, sizeof(int));
}

int getLeafPid(char* page, int pid)
{
	int count;
	// the first four bytes of a page contains # records in the page
	memcpy(&count, page+(PageFile::PAGE_SIZE-sizeof(int)), sizeof(int));
	return count;
}

static void setLeafPid(char* page, int pid)
{
	memcpy(page+(PageFile::PAGE_SIZE-sizeof(int)), &pid, sizeof(int));
}
	

//nonleaf helper functions
//
//
//
//


static char* NonLeafPtr(char* page, int n) 
{
	return (page+sizeof(int))+sizeof(int) + (sizeof(int)+sizeof(int))*n;
}


static void readNonLeaf(const char* page, int n, int& key, PageId& pid)
{
	// compute the location of the record
	char *ptr = NonLeafPtr(const_cast<char*>(page), n);
	
	// read the PageId
	memcpy(&pid, ptr, sizeof(int));
	
	// read the key
	memcpy(&key, ptr + sizeof(int), sizeof(int));
}

static void writeNonLeaf(char* page, int n, int key, PageId& pid)
{
	// compute the location of the record
	char *ptr = NonLeafPtr(page, n);
	
	//store the PageId
	memcpy(ptr, &pid, sizeof(int));
		
	// store the key
	memcpy(ptr + sizeof(int), &key, sizeof(int));
	
}

static void setNonLeafCount(char* page, int count)
{
	memcpy(page, &count, sizeof(int));
}

static void getNonLeafPid(char* page, int& pid)
{
	// the last four bytes of a page contains # records in the page
	memcpy(&pid, page+((PageFile::PAGE_SIZE)-sizeof(int)), sizeof(int));
}

static void setNonLeafPid(char* page, int pid)
{
	memcpy(page+((PageFile::PAGE_SIZE)-sizeof(int)), &pid, sizeof(int));
}

/*
bool isLeafNode(PageId pid, PageFile pf)
{
	int isleaf;
	char buffer2[PageFile::PAGE_SIZE];
	pf.read(pid, buffer2);
	memcpy(&isleaf, (buffer2+sizeof(int)), sizeof(int)); //copies if it is a leaf
	if (isleaf==42)
	{
		cout << "isleaf: true" << endl;
		return true;
	}
	//cout << pid << endl;
	cout << isleaf << endl;
	return false;
}
*/
